﻿//-------------------------------------------------------------------------------------------------
// <copyright file="WondrousItemEntryBuilder.cs" company="Anthony Adam">
// Copyright (c) Anthony Adam 2012. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------
// ReSharper disable once CheckNamespace
namespace SuperStarRounds
{
  using System;
  using System.Collections.Generic;
  using System.Text;

  /// <summary>
  /// This class handles the building of the Wondrous Item template as at RPG Superstar 2012
  /// </summary>
  public static class WondrousItemEntryBuilder
  {
    /// <summary>
    /// The build area is created just once for performance reasons.
    /// </summary>
    private static readonly StringBuilder Entry = new StringBuilder();

    /// <summary>
    /// Gets the full entry string.
    /// </summary>
    public static string EntryString => Entry.ToString();

    /// <summary>
    /// Resets the entry to build a new build - call this first on any build event triggers
    /// </summary>
    public static void StartNewEntry()
    {
      Entry.Length = 0;
    }

    /// <summary>
    /// Appends the name line to the entry - call after a call to StartNewEntry
    /// </summary>
    /// <param name="entryName">Text of the entry name without any formatting</param>
    public static void AppendEntryName(string entryName)
    {
      // Wondrous Item name
      Entry.Append($"[b]{entryName}[/b]");
      Entry.AppendLine();
    }

    /// <summary>
    /// Adds up to four auras to the entry with correct bracketting, commas and formatting.
    /// </summary>
    /// <param name="strengthAura1">The strength "faint", "moderate" or "strong" of this first aura</param>
    /// <param name="schoolAura1">The first aura string.</param>
    /// <param name="subSchoolAura1">The first aura post fix string or string.Empty</param>
    /// <param name="strengthAura2">The strength of the second aura if different to the first, else send string.Empty</param>
    /// <param name="schoolAura2">The second aura string.</param>
    /// <param name="subSchoolAura2">The second aura post fix string or string.Empty</param>
    /// <param name="strengthAura3">The strength of the third aura if different to the first two, else send string.Empty</param>
    /// <param name="schoolAura3">The third aura string.</param>
    /// <param name="subSchoolAura3">The third aura post fix string or string.Empty</param>
    /// <param name="strengthAura4">The strength of the fourth aura if different to the first three, else send string.Empty</param>
    /// <param name="schoolAura4">The fourth aura string.</param>
    /// <param name="subSchoolAura4">The fourth aura post fix string or string.Empty</param>
    public static void AppendAuras(string strengthAura1, string schoolAura1, string subSchoolAura1, string strengthAura2, string schoolAura2, string subSchoolAura2, string strengthAura3, string schoolAura3, string subSchoolAura3, string strengthAura4, string schoolAura4, string subSchoolAura4)
    {
      Entry.Append($"[b]Aura[/b] {strengthAura1} {schoolAura1}");
      
      AppendAuraSubSchool(subSchoolAura1);

      if (schoolAura2.Equals(string.Empty)) return;

      // do we put comma or and before second aura
      if (schoolAura3.Equals(string.Empty))
      {
        // no aura 3, so and and close
        Entry.Append(" and ");
        AppendSecondaryAuraAndStrength(strengthAura1, strengthAura2, schoolAura2, subSchoolAura2);
      }
      else
      {
        // we have an aura 3, so do a comma
        Entry.Append(", ");
        AppendSecondaryAuraAndStrength(strengthAura1, strengthAura2, schoolAura2, subSchoolAura2);

        // check for aura 4 and add aura 3
        if (schoolAura4.Equals(string.Empty))
        {
          // no aura 4, so and and close
          Entry.Append(" and ");
          AppendSecondaryAuraAndStrength(strengthAura2, strengthAura3, schoolAura3, subSchoolAura3);
        }
        else
        {
          // we have an aura 4, so do a comma
          Entry.Append(", ");
          AppendSecondaryAuraAndStrength(strengthAura2, strengthAura3, schoolAura3, subSchoolAura3);

          // add aura 4 and stop
          Entry.Append(" and ");
          AppendSecondaryAuraAndStrength(strengthAura3, strengthAura4, schoolAura4, subSchoolAura4);
        }
      }
    }
    
    /// <summary>
    /// Adds the caster level to the entry.
    /// </summary>
    /// <param name="casterLevel">The caster level string</param>
    public static void AppendCasterLevel(string casterLevel)
    {
      // Wondrous Item name
      Entry.Append($"; [b]CL[/b] {casterLevel}");
      Entry.AppendLine();
    }

    /// <summary>
    /// Adds the slot to the entry
    /// </summary>
    /// <param name="slot">The slot name</param>
    public static void AppendSlot(string slot)
    {
      // Wondrous Item name
      Entry.Append($"[b]Slot[/b] {slot}");
    }
    
    /// <summary>
    /// Adds up to 5 prices to the entry, if a price is zero, it stops at the first zero encountered.
    /// </summary>
    /// <param name="price1">First Price</param>
    /// <param name="pricePostFix1">First Price Qualifier text</param>
    /// <param name="price2">Second Price</param>
    /// <param name="pricePostFix2">Second Price Qualifier text</param>
    /// <param name="price3">Third Price</param>
    /// <param name="pricePostFix3">Third Price Qualifier text</param>
    /// <param name="price4">Fourth Price</param>
    /// <param name="pricePostFix4">Fourth Price Qualifier text</param>
    /// <param name="price5">Fifth Price</param>
    /// <param name="pricePostFix5">Fifth Price Qualifier text</param>
    public static void AppendPrice(decimal price1, string pricePostFix1, decimal price2, string pricePostFix2, decimal price3, string pricePostFix3, decimal price4, string pricePostFix4, decimal price5, string pricePostFix5)
    {
      Entry.Append($"; [b]Price[/b] {price1.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix1);

      if (price2 == 0) return;

      Entry.Append($", {price2.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix2);

      if (price3 == 0) return;

      Entry.Append($", {price3.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix3);

      if (price4 == 0) return;

      Entry.Append($", {price4.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix4);

      if (price5 == 0) return;

      Entry.Append($", {price5.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix5);
    }
    
    /// <summary>
    /// Adds the weight portion to the entry.
    /// </summary>
    /// <param name="weight">The weight - if a number, lb. or lbs. is determined and added automatically, else just the string provided is used.</param>
    public static void AppendWeight(string weight)
    {
      double myWeight;
      var canParse = double.TryParse(weight, out myWeight);
      Entry.Append($"; [b]Weight[/b] {(canParse ? myWeight.ToString("##,#") : weight)}");
      if (canParse) Entry.Append(myWeight > 1.0 ? "lbs." : "lb.");
      Entry.AppendLine();
    }
    
    /// <summary>
    /// Adds the Description header block line.
    /// </summary>
    /// <param name="line">Usually "Description" without bb tags but parameterised for future format changes.</param>
    public static void AppendDescriptionLine(string line)
    {
      Entry.Append($"[b]{line}[/b]");
      Entry.AppendLine();
    }
    
    /// <summary>
    /// Adds the Construction header block line.
    /// </summary>
    /// <param name="line">Usually "Construction" without bb tags but parameterised for future format changes.</param>
    public static void AppendConstructionLine(string line)
    {
      Entry.Append($"[b]{line}[/b]");
      Entry.AppendLine();
    }
    
    /// <summary>
    /// Adds up to 3 construction feats, stopping at the first enpty string.
    /// </summary>
    /// <param name="constructionFeat1">Feat 1</param>
    /// <param name="constructionFeat2">Feat 2</param>
    /// <param name="constructionFeat3">Feat 3</param>
    public static void AppendConstructionFeats(string constructionFeat1, string constructionFeat2, string constructionFeat3)
    {
      Entry.Append("[b]Requirements[/b] ");

      var feats = new List<string> {constructionFeat1};

      if (!constructionFeat2.Equals(string.Empty))
      {
        feats.Add(constructionFeat2);
        if (!constructionFeat3.Equals(string.Empty))
          feats.Add(constructionFeat3);
      }

      feats.Sort((feat1, feat2) => string.Compare(feat1, feat2, StringComparison.Ordinal));

      Entry.Append(feats[0]);
      if (feats.Count <= 1 || feats[1].Equals(string.Empty)) return;

      Entry.Append(", ");
      Entry.Append(feats[1]);

      if (feats.Count <= 2 || feats[2].Equals(string.Empty)) return;

      Entry.Append(", ");
      Entry.Append(feats[2]);
    }
    
    /// <summary>
    /// Adds the spells to the entry in the provide sorted list. This just dumps the spells, so be sure you pass
    /// teh alphabetised and lowercased spells in the list parameter.
    /// </summary>
    /// <param name="spellList">List of Spells in alphabetical order</param>
    public static void AppendConstructionSpells(List<WondrousItemSpellChosen> spellList)
    {
      foreach (var spell in spellList)
      {
        if (!spell.SpellSpell.Equals(string.Empty))
          Entry.Append($", [i]{spell.SpellSpell}[/i]");
      }
    }

    /// <summary>
    /// This adds the construction skills, other text after the spells but before the cost element.
    /// </summary>
    /// <param name="skillLineReq">Set to true if adding skill requirement to construction</param>
    /// <param name="ranks">Required skill ranks</param>
    /// <param name="skill">Skill Name</param>
    /// <param name="subSkill">Sub-Skill Name</param>
    /// <param name="raceLineReq">Set to true if race requirement is added to construction</param>
    /// <param name="race">Race Name</param>
    /// <param name="textLinesManual">Set to true to include manual construction text</param>
    /// <param name="textLines">Text to include</param>
    public static void AppendConstructionOther(bool skillLineReq, string ranks, string skill, string subSkill, bool raceLineReq, string race, bool textLinesManual, string textLines)
    {
      // TODO: Localise controls, add controls to save / load
      if (skillLineReq)
      {
        if (string.IsNullOrEmpty(subSkill) || string.IsNullOrWhiteSpace(subSkill))
          Entry.Append($", creator must have {ranks} ranks in the {skill} skill");
        else
          Entry.Append($", creator must have {ranks} ranks in the {skill} ({subSkill}) skill");
      }

      if (raceLineReq) Entry.Append($", creator must be {race}");

      if (textLinesManual) Entry.Append($", {textLines}");
    }

    /// <summary>
    /// Appends the Cost elements in the same manner as the prices
    /// </summary>
    /// <param name="cost1">First Cost</param>
    /// <param name="pricePostFix1">First Cost Qualifier text - matches the Price Qualifier for the related price</param>
    /// <param name="cost2">Second Cost</param>
    /// <param name="pricePostFix2">Second Cost Qualifier text - matches the Price Qualifier for the related price</param>
    /// <param name="cost3">Third Cost</param>
    /// <param name="pricePostFix3">Third Cost Qualifier text - matches the Price Qualifier for the related price</param>
    /// <param name="cost4">Fourth Cost</param>
    /// <param name="pricePostFix4">Fourth Cost Qualifier text - matches the Price Qualifier for the related price</param>
    /// <param name="cost5">Fifth Cost</param>
    /// <param name="pricePostFix5">Fifth Cost Qualifier text - matches the Price Qualifier for the related price</param>
    public static void AppendCost(decimal cost1, string pricePostFix1, decimal cost2, string pricePostFix2, decimal cost3, string pricePostFix3, decimal cost4, string pricePostFix4, decimal cost5, string pricePostFix5)
    {
      Entry.Append($"; [b]Cost[/b] {cost1.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix1);

      if (cost2 == 0) return;

      Entry.Append($", {cost2.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix2);

      if (cost3 == 0) return;

      Entry.Append($", {cost3.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix3);

      if (cost4 == 0) return;

      Entry.Append($", {cost4.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix4);

      if (cost5 == 0) return;

      Entry.Append($", {cost5.ToString("##,#")} gp");
      AppendPricePostFix(pricePostFix5);
    }
    
    /// <summary>
    /// Adds the closing line break to the entry. Call this before extracting the entry string when its a new built entry.
    /// </summary>
    /// <returns>The entry string in BB encoded and formatted content</returns>
    public static string CloseOffLastLine()
    {
      Entry.AppendLine();
      return Entry.ToString();
    }
    
    /// <summary>
    /// Utility formatter for Aura and use of , or and separators.
    /// </summary>
    /// <param name="strengthAura1">The prior aura strength</param>
    /// <param name="strengthAura2">The aura strength to be added (if different to the first)</param>
    /// <param name="schoolAura2">The school of the aura</param>
    /// <param name="subSchoolAura2">The aura qualification (if any)</param>
    private static void AppendSecondaryAuraAndStrength(string strengthAura1, string strengthAura2, string schoolAura2, string subSchoolAura2)
    {
      AppendSecondaryStrength(strengthAura1, strengthAura2);
      Entry.Append(schoolAura2);
      AppendAuraSubSchool(subSchoolAura2);
    }
    
    /// <summary>
    /// Utility formatter for Aura Strengths.
    /// </summary>
    /// <param name="strengthAura1">The prior aura strength</param>
    /// <param name="strengthAura2">The aura strength to be added (if different to the first)</param>
    private static void AppendSecondaryStrength(string strengthAura1, string strengthAura2)
    {
      if (!strengthAura2.Equals(string.Empty))
        Entry.Append(strengthAura1.Equals(strengthAura2) ? string.Empty : strengthAura2 + " ");
    }
    
    /// <summary>
    /// Utility formatter for Aura SubSchool / Qualifications.
    /// It will automatically round bracket wrap the sub school unless it is already bracketed by square or round brackets.
    /// </summary>
    /// <param name="subSchool">The qualifier of the aura (use empty.String if no qualifier)</param>
    private static void AppendAuraSubSchool(string subSchool)
    {
      if (!subSchool.Equals(string.Empty))
        Entry.AppendFormat(subSchool.StartsWith("[") || subSchool.StartsWith("(") ? " {0}" : " ({0})", subSchool);
    }
    
    /// <summary>
    /// Utility formatter for a price postfix
    /// It will automatically round bracket wrap the qualifier unless it is already round bracketed.
    /// </summary>
    /// <param name="pricePostFix">The qualifier of the price (use empty.String if no qualifier)</param>
    private static void AppendPricePostFix(string pricePostFix)
    {
      if (!pricePostFix.Equals(string.Empty))
        Entry.AppendFormat(pricePostFix.StartsWith("(") ? " {0}" : " ({0})", pricePostFix);
    }    
  }
}